package com.keyue.service.admin.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.keyue.common.constant.Constant;
import com.keyue.common.constant.ServerResult;
import com.keyue.common.constant.ServerResultCode;
import com.keyue.common.exception.BusinessException;
import com.keyue.common.utils.ImgCaptchaUtils;
import com.keyue.common.utils.SmsUtils;
import com.keyue.common.utils.TransactionHelper;
import com.keyue.common.utils.UserUtils;
import com.keyue.config.DataScope;
import com.keyue.domain.DataPermissionRedisVO;
import com.keyue.domain.RoleEnum;
import com.keyue.domain.UserInfoRedisVO;
import com.keyue.domain.dto.BaseDO;
import com.keyue.domain.dto.DataPermissionDO;
import com.keyue.domain.dto.admin.*;
import com.keyue.domain.vo.UserVO;
import com.keyue.domain.vo.admin.*;
import com.keyue.mapper.admin.AdminUserMapper;
import com.keyue.security.TokenProvider;
import com.keyue.security.config.SecurityProperties;
import com.keyue.security.service.OnlineUserService;
import com.keyue.security.vo.JwtUser;
import com.keyue.service.DataPermissionService;
import com.keyue.service.admin.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 用户表(AdminUser)表服务实现类
 *
 * @author makejava
 * @since 2023-08-30 17:51:48
 */
@Slf4j
@Service
public class AdminUserServiceImpl extends ServiceImpl<AdminUserMapper, AdminUserDO> implements AdminUserService {

    @Resource
    private ImgCaptchaUtils imgCaptchaUtils;

    @Resource
    private SmsUtils smsUtils;

    @Resource
    private AuthenticationManager authenticationManager;

    @Resource
    private TokenProvider tokenProvider;

    @Resource
    private OnlineUserService onlineUserService;


    @Resource
    private SecurityProperties securityProperties;

    @Resource
    private AdminUserJobService userJobService;

    @Resource
    private AdminJobService jobService;

    @Resource
    private AdminDeptService deptService;

    @Resource
    private AdminUserRoleService userRoleService;

    @Resource
    private AdminRoleService roleService;

    @Resource
    private PasswordEncoder passwordEncoder;

    @Resource
    private TransactionHelper transactionHelper;

    @Resource
    private DataScope dataScope;

    @Resource
    private DataPermissionService dataPermissionService;

    @Resource
    private UserUtils userUtils;

    /**
     * 通过ID查询单条数据
     * <p>
     * adminUserQueryVO 主键
     *
     * @return 实例对象
     */
    @Override
    public ServerResult queryById(AdminUserQueryVO adminUserQueryVO) {
        if (Objects.isNull(adminUserQueryVO.getId())) {
            return ServerResult.fail("id 不能为空");
        }
        AdminUserDO AdminUserDo = getById(adminUserQueryVO.getId());
        if (Objects.isNull(AdminUserDo)) {
            return ServerResult.success(null);
        }
        return ServerResult.success(BeanUtil.copyProperties(AdminUserDo, AdminUserVO.class));
    }

    /**
     * 分页查询
     *
     * @param queryVO 筛选条件
     * @return 查询结果
     */
    @Override
    public ServerResult list(AdminUserQueryVO queryVO) {

        LambdaQueryWrapper<AdminUserDO> queryWrapper = buildQueryByParam(queryVO);
        Page page = new Page<>(queryVO.getPage(), queryVO.getPageSize());
        IPage<AdminUserDO> iPage = page(page, queryWrapper);

        Page<AdminUserResultVO> webVOPage = new Page<>();
        List<AdminUserDO> adminDeptDOS = iPage.getRecords();
        Map<Long, AdminUserRoleDO> userRoleDOMap = new HashMap<>();
        if (CollectionUtils.isNotEmpty(adminDeptDOS)) {
            Set<Long> userIds = adminDeptDOS.stream().map(AdminUserDO::getId).collect(Collectors.toSet());
            List<AdminUserRoleDO> userRoleDOList = userRoleService.list(new LambdaQueryWrapper<AdminUserRoleDO>()
                    .in(AdminUserRoleDO::getUserId, userIds));
            if (CollectionUtils.isNotEmpty(userRoleDOList)) {
                userRoleDOMap = userRoleDOList.stream().collect(Collectors.toMap(AdminUserRoleDO::getUserId, Function.identity()));
            }
            Map<Long, AdminUserRoleDO> finalUserRoleDOMap = userRoleDOMap;
            List<AdminUserResultVO> queryVos = adminDeptDOS.stream().map(x -> {
                AdminUserResultVO resultVO = new AdminUserResultVO();
                BeanUtils.copyProperties(x, resultVO);
                if (finalUserRoleDOMap.containsKey(x.getId())) {
                    AdminUserRoleDO userRoleDO = finalUserRoleDOMap.get(x.getId());
                    resultVO.setRoleId(userRoleDO.getRoleId());
                    resultVO.setRoleCode(RoleEnum.getCodeById(userRoleDO.getRoleId()));
                    resultVO.setRoleName(RoleEnum.getNameById(userRoleDO.getRoleId()));
                }
                return resultVO;
            }).collect(Collectors.toList());
            webVOPage.setRecords(queryVos);
        }
        BeanUtils.copyProperties(page, webVOPage, "records");
        return ServerResult.success(webVOPage);
    }

    /**
     * @param queryVO
     * @return
     */
    private LambdaQueryWrapper<AdminUserDO> buildQueryByParam(AdminUserQueryVO queryVO) {
        return new LambdaQueryWrapper<AdminUserDO>()
                .in(BaseDO::getEnableFlag, Arrays.asList(Constant.ENABLE_TEN, Constant.THIRTY))
                .eq(StringUtils.isNotBlank(queryVO.getUserName()), AdminUserDO::getUserName, queryVO.getUserName())
                .eq(StringUtils.isNotBlank(queryVO.getNickName()), AdminUserDO::getNickName, queryVO.getNickName())
                .eq(StringUtils.isNotBlank(queryVO.getPhone()), AdminUserDO::getPhone, queryVO.getPhone())
                .eq(StringUtils.isNotBlank(queryVO.getEmail()), AdminUserDO::getEmail, queryVO.getEmail());
    }

    /**
     * 新增数据
     *
     * @param adminUserVO 实例对象
     * @return 实例对象
     */
    @Override
    public ServerResult add(AdminUserVO adminUserVO) {
        if (Objects.nonNull(adminUserVO.getId())) {
            adminUserVO.setId(null);
        }
        List<AdminUserDO> userDOS = list(new LambdaQueryWrapper<AdminUserDO>().eq(BaseDO::getEnableFlag, Constant.ENABLE_TEN)
                .eq(AdminUserDO::getPhone, adminUserVO.getPhone()));
        if (CollectionUtils.isNotEmpty(userDOS)) {
            throw new BusinessException("手机号已被注册");
        }
        AdminUserDO adminUserDo = BeanUtil.copyProperties(adminUserVO, AdminUserDO.class);
        if (StringUtils.isNotBlank(adminUserVO.getUserName())) {
            adminUserDo.setNickName(adminUserVO.getUserName());
        }
        if (StringUtils.isNotBlank(adminUserVO.getNickName())) {
            adminUserDo.setUserName(adminUserVO.getNickName());
        }
        adminUserDo.setGender("男");
        if (Objects.isNull(adminUserDo.getDeptId())) {
            adminUserDo.setDeptId(1L);
        }
        adminUserDo.setIsAdmin(true);
        adminUserDo.setPassword(passwordEncoder.encode("123456"));
        save(adminUserDo);

        if (Objects.nonNull(adminUserVO.getRoleId())) {
            Set<Long> roleIds = new HashSet<>();
            roleIds.add(adminUserVO.getRoleId());
            authRole(AdminAuthUserVO.builder().userId(adminUserDo.getId()).roleIds(roleIds).build());
        }
        return ServerResult.success();
    }

    @Override
    public ServerResult authRole(AdminAuthUserVO adminUserVO) {
        AdminUserDO userDO = getById(adminUserVO.getUserId());
        if (Objects.isNull(userDO)) {
            return ServerResult.fail("用户不存在");
        }
        List<AdminUserRoleDO> list = adminUserVO.getRoleIds().stream().map(x -> {
            return AdminUserRoleDO.builder().userId(adminUserVO.getUserId()).roleId(x).build();
        }).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(list)) {
            return ServerResult.success();
        }
        try {
            transactionHelper.run(() -> {
                userRoleService.remove(new LambdaQueryWrapper<AdminUserRoleDO>().eq(AdminUserRoleDO::getUserId, adminUserVO.getUserId()));
                userRoleService.saveBatch(list);
            });
        } catch (Exception e) {
            log.error("授权异常：e:{}", e.getMessage());
        }

        return ServerResult.success();
    }

    /**
     * 修改数据
     *
     * @param adminUserVO 实例对象
     * @return 实例对象
     */
    @Override
    public ServerResult edit(AdminUserVO adminUserVO) {
        //todo 校验
        if (Objects.isNull(adminUserVO.getId())) {
            return ServerResult.fail("id 不能为空");
        }
        AdminUserDO adminUserDo = BeanUtil.copyProperties(adminUserVO, AdminUserDO.class);
        adminUserDo.setUserName(adminUserDo.getNickName());
        updateById(adminUserDo);
        if (Objects.nonNull(adminUserVO.getRoleId())) {
            Set<Long> roleIds = new HashSet<>();
            roleIds.add(adminUserVO.getRoleId());
            authRole(AdminAuthUserVO.builder().userId(adminUserDo.getId()).roleIds(roleIds).build());
        }
        return ServerResult.success();
    }

    @Override
    public ServerResult<AdminResultVO> isAdmin(String userName) {
        UserVO user = findByUserName(userName);
        if (Objects.isNull(user)) {
            throw new BusinessException("用户不存在");
        }
        List<AdminUserRoleDO> userRoleDOList = userRoleService.list(new LambdaQueryWrapper<AdminUserRoleDO>()
                .eq(AdminUserRoleDO::getUserId, user.getId()));
        if (CollectionUtils.isEmpty(userRoleDOList)) {
            return ServerResult.success(AdminResultVO.builder().isAdmin(false).userVO(user).build());
        }
        AtomicReference<Boolean> result = new AtomicReference<>(false);
        userRoleDOList.forEach(x -> {
            if (RoleEnum.ADMIN.getId().equals(x.getRoleId())) {
                result.set(true);
            }
        });
        return ServerResult.success(AdminResultVO.builder().isAdmin(result.get()).userVO(user).build());
    }


    @Override
    public ServerResult<RoleEnum> getRoleBy(String userName) {
        UserVO user = findByUserName(userName);
        if (Objects.isNull(user)) {
            throw new BusinessException("用户不存在");
        }
        List<AdminUserRoleDO> userRoleDOList = userRoleService.list(new LambdaQueryWrapper<AdminUserRoleDO>()
                .eq(AdminUserRoleDO::getUserId, user.getId()));
        if (CollectionUtils.isEmpty(userRoleDOList)) {
            throw new BusinessException("用户没有创建角色");
        }
        AtomicReference<RoleEnum> result = new AtomicReference<RoleEnum>(RoleEnum.ADMIN);
        userRoleDOList.forEach(x -> {
            if (RoleEnum.ADMIN.getId().equals(x.getRoleId())) {
                result.set(RoleEnum.ADMIN);
            }
            if (RoleEnum.BUSINESS_USER.getId().equals(x.getRoleId())) {
                result.set(RoleEnum.BUSINESS_USER);
            }
            if (RoleEnum.TRACKER_USER.getId().equals(x.getRoleId())) {
                result.set(RoleEnum.TRACKER_USER);
            }
            if (RoleEnum.INSPECT_USER.getId().equals(x.getRoleId())) {
                result.set(RoleEnum.INSPECT_USER);
            }
        });
        return ServerResult.success(result.get());
    }

    @Override
    public ServerResult opt(AdminUserQueryVO adminUserQueryVO) {
        List<Long> ids = adminUserQueryVO.getIds();
        if (CollUtil.isEmpty(ids)) {
            return ServerResult.fail("ids 参数不能为空");
        }
        List<AdminUserDO> adminUserList = listByIds(ids);
        if (CollUtil.isEmpty(adminUserList)) {
            return ServerResult.success();
        }
        LambdaUpdateWrapper<AdminUserDO> updateWrapper = new LambdaUpdateWrapper<>();
        if (adminUserQueryVO.getOptType() == Constant.THIRTY) {
            updateWrapper.in(AdminUserDO::getId, ids).set(AdminUserDO::getStatus, Constant.UNABLE_TWENTY);
        } else if (adminUserQueryVO.getOptType() == Constant.UNABLE_TWENTY) {
            updateWrapper.in(AdminUserDO::getId, ids).set(AdminUserDO::getEnableFlag, Constant.UNABLE_TWENTY);
        } else {
            updateWrapper.in(AdminUserDO::getId, ids).set(AdminUserDO::getStatus, Constant.ENABLE_TEN);
        }
        update(updateWrapper);
        return ServerResult.success();
    }

    /**
     * 通过主键删除数据
     *
     * @param adminUserQueryVO 筛选条件
     * @return 是否成功
     */
    @Override
    public ServerResult deleteById(AdminUserQueryVO adminUserQueryVO) {
        List<Long> ids = adminUserQueryVO.getIds();
        if (CollUtil.isEmpty(ids)) {
            return ServerResult.fail("ids 参数不能为空");
        }
        List<AdminUserDO> adminUserList = listByIds(ids);
        if (CollUtil.isEmpty(adminUserList)) {
            return ServerResult.success();
        }
        //todo 校验是否可以逻辑删除
        LambdaUpdateWrapper<AdminUserDO> updateWrapper = new LambdaUpdateWrapper<AdminUserDO>()
                .in(AdminUserDO::getId, ids)
                .set(AdminUserDO::getEnableFlag, Constant.UNABLE_TWENTY);
        update(updateWrapper);
        return ServerResult.success();
    }

    @Override
    public UserVO findByUserName(String userName) {
        AdminUserDO userDO = getOne(new LambdaQueryWrapper<AdminUserDO>()
                .in(BaseDO::getEnableFlag, Arrays.asList(Constant.ENABLE_TEN, Constant.THIRTY))
                .and(q -> q.eq(AdminUserDO::getUserName, userName).or().eq(AdminUserDO::getPhone, userName))
                .last("limit 1"));
        if (Objects.isNull(userDO)) {
            throw new BusinessException(ServerResultCode.USER_NOT_EXIT);
        }
        UserVO userVO = new UserVO();
        BeanUtils.copyProperties(userDO, userVO);
        userVO.setEnabled(Constant.ENABLE_TEN == userDO.getEnableFlag());
        AdminUserJobDO userJobDO = userJobService.getOne(new LambdaQueryWrapper<AdminUserJobDO>().eq(AdminUserJobDO::getUserId, userDO.getId()));
        if (Objects.nonNull(userJobDO)) {
            AdminJobDO jobDO = jobService.getById(userJobDO.getJobId());
            userVO.setJob(JobSmallVO.builder().id(jobDO.getId()).name(jobDO.getName()).build());
        }
        AdminDeptDO deptDO = deptService.getById(userDO.getDeptId());
        if (Objects.nonNull(deptDO)) {
            userVO.setDept(DeptSmallVO.builder().id(deptDO.getId()).name(deptDO.getName()).build());
        }
        List<AdminUserRoleDO> userRoleDOList = userRoleService.list(new LambdaQueryWrapper<AdminUserRoleDO>().eq(AdminUserRoleDO::getUserId, userDO.getId()));
        if (CollectionUtils.isNotEmpty(userRoleDOList)) {
            Set<Long> roleIds = userRoleDOList.stream().map(AdminUserRoleDO::getRoleId).collect(Collectors.toSet());
            List<AdminRoleDO> roleDOList = roleService.listByIds(roleIds);
            if (CollectionUtils.isNotEmpty(roleDOList)) {
                userVO.setRoles(roleDOList.stream().map(x -> {
                    return RoleSmallVO.builder().id(x.getId()).name(x.getName()).dataScope(x.getDataScope()).level(x.getLevel()).build();
                }).collect(Collectors.toSet()));
            }
        }
        return userVO;
    }


    @Override
    public ServerResult phoneRegister(AdminUserRegisterVO registerVO) {
        return null;
    }

    @Override
    public ServerResult loginByPhone(AdminUserLoginByPhoneVO loginByPhoneVO) {
        return null;
    }

    @Override
    public ServerResult loginByPwd(AdminUserLoginByPwdVO loginByPwdVO) {
        imgCaptchaUtils.validateImgCaptcha(loginByPwdVO.getImgCaptchaCode(), loginByPwdVO.getImgCaptchaUuid());

        return ServerResult.success(buildLoginUser(loginByPwdVO));
    }

    @Override
    public ServerResult updatePass(UpdatePwdVO pwdVO) {
        if (!pwdVO.getPassword().equals(pwdVO.getConfirmPassword())) {
            throw new BusinessException(ServerResultCode.MEMBER_CONFIRM_PASSWORD_ERROR);
        }
        AdminUserDO userDO = getById(pwdVO.getUserId());
        if (Objects.isNull(userDO)) {
            throw new BusinessException(ServerResultCode.USER_NOT_EXIT);
        }
        userDO.setPwdResetTime(LocalDateTime.now());
        userDO.setPassword(passwordEncoder.encode(pwdVO.getPassword()));
        updateById(userDO);
        return ServerResult.success();
    }

    @Override
    public ServerResult resetPwdVO(ResetPwdVO resetPwdVO) {
        AdminUserDO userDO = getById(resetPwdVO.getUserId());
        if (Objects.isNull(userDO)) {
            throw new BusinessException(ServerResultCode.USER_NOT_EXIT);
        }
        userDO.setPwdResetTime(LocalDateTime.now());
        userDO.setPassword(passwordEncoder.encode("123456"));
        updateById(userDO);
        return ServerResult.success();
    }

    /**
     * @param loginByPwdVO
     * @return
     */
    private Map<String, Object> buildLoginUser(AdminUserLoginByPwdVO loginByPwdVO) {
        Authentication authentication = auth(loginByPwdVO);
        // 生成令牌
        String token = tokenProvider.createToken(authentication);
        final JwtUser jwtUser = (JwtUser) authentication.getPrincipal();
        // 保存在线信息
        onlineUserService.save(jwtUser, token);
        // 返回 token 与 用户信息
        Map<String, Object> authInfo = new HashMap<String, Object>(2) {{
            put("token", securityProperties.getTokenStartWith() + token);
            put("user", jwtUser);
        }};

        UserInfoRedisVO userInfoRedisVO = buildUserInfoRedisVO(jwtUser);
        List<DataPermissionDO> dataPermissionDOS = dataPermissionService.list(new LambdaQueryWrapper<DataPermissionDO>().eq(BaseDO::getEnableFlag, Constant.ENABLE_TEN).eq(DataPermissionDO::getUserId, jwtUser.getId()));
        if (CollectionUtils.isNotEmpty(dataPermissionDOS)) {
            userInfoRedisVO.setDataPermissionRedisVOS(dataPermissionDOS.stream().map(x -> {
                DataPermissionRedisVO dataPermissionRedisVO = new DataPermissionRedisVO();
                dataPermissionRedisVO.setModelType(x.getModelType());
                dataPermissionRedisVO.setDataPermissionItemVOJson(x.getDataPermissionLineJson());
                return dataPermissionRedisVO;
            }).collect(Collectors.toList()));
        }
        userUtils.cacheUserLoginInfo(userInfoRedisVO);
        return authInfo;
    }

    private UserInfoRedisVO buildUserInfoRedisVO(JwtUser jwtUser) {
        UserInfoRedisVO userInfoRedisVO = new UserInfoRedisVO();
        BeanUtils.copyProperties(jwtUser, userInfoRedisVO);
        return userInfoRedisVO;
    }

    /**
     * @param loginByPwdVO
     * @return
     */
    private Authentication auth(AdminUserLoginByPwdVO loginByPwdVO) {

        UsernamePasswordAuthenticationToken authenticationToken =
                new UsernamePasswordAuthenticationToken(loginByPwdVO.getUserName(), loginByPwdVO.getPassword());

        Authentication authentication = authenticationManager.authenticate(authenticationToken);
        SecurityContextHolder.getContext().setAuthentication(authentication);

        return authentication;
    }

    @Override
    public ServerResult passwordModify(AdminUserModifyPwdVO modifyPwdVO) {
        smsUtils.validateSmsCaptcha(modifyPwdVO.getPhone(), modifyPwdVO.getSmsCaptchaCode());
        if (!modifyPwdVO.getPassword().equals(modifyPwdVO.getConfirmPassword())) {
            throw new BusinessException(ServerResultCode.MEMBER_CONFIRM_PASSWORD_ERROR);
        }
        AdminUserDO userDO = getOne(new LambdaQueryWrapper<AdminUserDO>()
                .eq(BaseDO::getEnableFlag, Constant.ENABLE_TEN)
                .eq(AdminUserDO::getStatus, Constant.ENABLE_TEN)
                .eq(AdminUserDO::getPhone, modifyPwdVO.getPhone()).last("limit 1"));
        if (Objects.isNull(userDO)) {
            throw new BusinessException(ServerResultCode.USER_NOT_EXIT);
        }
        userDO.setPwdResetTime(LocalDateTime.now());
        userDO.setPassword(passwordEncoder.encode(modifyPwdVO.getPassword()));
        updateById(userDO);
        return ServerResult.success();
    }

    @Override
    public ServerResult sendPasswordRecoverMail(AdminUserPasswordRecoverEmailVO recoverEmailVO) {
        return null;
    }

    @Override
    public MemberUserVO findByNameOrPhone(String username) {
        return null;
    }
}
